from django.db import models
from django.db.models.query_utils import Q
from django.core.urlresolvers import reverse
import pickle, base64
from facebookdj import get_graph_client, get_rest_client
from facebook.rest import FacebookError
from szachy.chess import MATE, WHITE, STALEMATE
from django.utils.safestring import mark_safe
from django.conf import settings

MY_FB_APP_URL = 'http://apps.facebook.com/%s/' % settings.FACEBOOK_APP_NAME

class FBUserManager(models.Manager):
    
    def get_by_id(self, id):
        graph = get_graph_client()
        user, created = self.get_or_create(id=id)
        if not user.name:
            profile = graph.get_object(str(id))
            user.name = profile['name']
            user.profile = profile['link']
            user.photo = "https://graph.facebook.com/%s/picture/" % id
            
            user.save()            
        return user
    
    def get_current(self):
        """Gets a User object for the logged-in Facebook user."""
        return self.get_by_id(int(get_graph_client().uid))

class FBUser(models.Model):
    id = models.IntegerField(primary_key=True)
    ranking = models.IntegerField(default=1000)
    
    name = models.CharField(max_length=250)
    photo = models.URLField()
    profile = models.URLField()
    
    objects = FBUserManager()
    
    def __unicode__(self):
        return mark_safe('<a href="%s"><img src="%s"/>%s</a>' % (self.profile, self.photo,self.name))
    
    def games(self):
        return Game.objects.games(self.id, True)
    
    def games_history(self):
        return Game.objects.games_history(self.id)
    
    def get_wait_action_count(self):
        '''
        count number of wait action (need set or make move)
        '''
        count = 0
        count += self.games().filter(current_move=self.id).count()
        count += Game.objects.filter(state=Game.STATE_CREATED, p1=self, p1_state=Game.PLAYER_UNKNOWN_SIDE).count()
        count += Game.objects.filter(state=Game.STATE_CREATED, p2=self, p2_state=Game.PLAYER_UNKNOWN_SIDE).count()
        return count

class GameManager(models.Manager):
    def play(self, p1, p2, commit=True):
        '''
        create new Game between p1 and p2
        '''        
        # TODO: do not start new Game, if exists new not started game between two same players
        game = Game(p1=FBUser.objects.get_by_id(p1), p2=FBUser.objects.get_by_id(p2), state='c', p1_state='u', p2_state='u')
        game.save()
        rest = get_rest_client()
        game.game_invitation = rest.dashboard.addNews(uid=p2, news=[{'message': '{*actor*} wants to play with you', 'action_link': {'text': 'Show the board', 'href': '%smatch/%d/' % (MY_FB_APP_URL, game.id)}}])
        rest.dashboard.incrementCount(uid=p2)    
        game.save()
        return game
    
    def games_history(self, player, limit=10):
        return self.games(player, False).order_by('-creation_time')[:10] 
        
    def games(self, player, only_current=True):
        '''
        query for player's games
        '''
        q =  self.get_query_set().filter(Q(p1=player) | Q(p2=player))
        if only_current:
            q = q.filter(state__in=(Game.STATE_BEGINNED, Game.STATE_CREATED))
        else:
            q = q.filter(state__in=(Game.STATE_WHITE_WINS, Game.STATE_BLACK_WINS, Game.STATE_DRAW))
        return q

class Game(models.Model):
    '''
    Model represents one chess Game, p1 and p2 are fb id of players.
    '''
    STATE_CREATED = 'c'
    STATE_BEGINNED = 's'
    STATE_WHITE_WINS = 'w'
    STATE_BLACK_WINS = 'b'
    STATE_DRAW = 'd'
    STATE_CHOICES = (
            (STATE_CREATED, 'created'),
            (STATE_BEGINNED, 'beginned'),
            (STATE_WHITE_WINS, 'white wins'),
            (STATE_BLACK_WINS, 'black wins'),
            ('d', 'draw'),
        )
    PLAYER_UNKNOWN_SIDE = 'u'
    PLAYER_BLACK = 'b'
    PLAYER_WHITE = 'w'
    PLAYER_CHOICES = (
            (PLAYER_UNKNOWN_SIDE, 'Unknown'),
            (PLAYER_WHITE, 'Plays as white'),
            (PLAYER_BLACK, 'Plays as black')
        )
    #p1 = models.IntegerField(db_index=True, null=False, blank=False)
    #p2 = models.IntegerField(db_index=True, null=False, blank=False)
    p1 = models.ForeignKey(FBUser, related_name='p1_set', db_column='p1')
    p2 = models.ForeignKey(FBUser, related_name='p2_set', db_column='p2')
    
    state = models.CharField(max_length=1, choices=STATE_CHOICES, default=STATE_CREATED, null=False, blank=False)
    creation_time = models.DateTimeField(auto_now_add=True, null=False, blank=False)
    p1_state = models.CharField(max_length=1, choices=PLAYER_CHOICES, null=False, blank=False)
    p2_state = models.CharField(max_length=1, choices=PLAYER_CHOICES, null=False, blank=False)
    current_move = models.IntegerField(db_index=True, null=True, blank=True)
    pgn = models.TextField(blank=True)
    # new with game invitation
    game_invitation = models.IntegerField(blank=True, null=True)
    
    objects = GameManager()
    
    def __init__(self, *args, **kwargs):
        super(Game, self).__init__(*args, **kwargs)
        self._game = None
    
    def is_started(self):
        return not self.state == 'c'
    
    def decline_url(self):
        return reverse('decline_match', kwargs={'game_id': self.id})
    
    def configure_url(self):
        return reverse('configure_match', kwargs={'game_id': self.id})
    
    @property
    def white_player(self):
        if self.p1_state == self.PLAYER_WHITE:
            return self.p1
        elif self.p2_state == self.PLAYER_WHITE:
            return self.p2
    @property
    def black_player(self):
        if self.p1_state == self.PLAYER_BLACK:
            return self.p1
        elif self.p2_state == self.PLAYER_BLACK:
            return self.p2
        
    def opponent(self, you):
        if self.p1==you:
            return self.p2
        elif self.p2==you:
            return self.p1
        return None
    
    @property
    def game(self):        
        if not self._game:
            from szachy.chess import Game as ChessGame
            if not self.pgn:
                self._game = ChessGame()
                self._game.setup()
            else:
                self._game = pickle.loads(base64.decodestring(self.pgn))
            self._done_move = False
        return self._game
    
    def mark_done_move(self):
        '''
        if move is ok, change it
        '''
        self._done_move = True
    
    def save(self, *args, **kwargs):
        
        if self._game:
            # if somebody done a move, sava changes
            if self._done_move:
                self.pgn = base64.encodestring(pickle.dumps(self._game))
                
                
        super(Game, self).save(*args, **kwargs)
        
    def get_board_orientation(self):
        
        me = FBUser.objects.get_current()  
        opponent = self.opponent(me)
        
        black_on_bottom = False
        if opponent: 
            top_player = opponent
            bottom_player = me
            if self.black_player == me or self.white_player == opponent:
                black_on_bottom = True
        else:
            top_player = self.p1
            bottom_player = self.p2
        
        for player, state in ( (self.p1, self.p1_state), (self.p2, self.p2_state)):
            if state == self.PLAYER_UNKNOWN_SIDE:
                if player == top_player:
                    top_player = None
                if player == bottom_player:
                    bottom_player = None
                    
        return top_player, bottom_player, black_on_bottom
    
    def get_player_state(self, player):
        if self.p1 == player:
            return self.p1_state
        elif self.p2 == player:
            return self.p2_state
        raise Exception('Player %d did not play in this match!' % player)
    
    def get_my_state(self):
        return self.get_player_state(FBUser.objects.get_current())
    
    @property
    def is_ongoing(self):
        '''Returns ongoing state, update this state if game finished recently'''
        
        ongoing = True
        
        if self.state == Game.STATE_BEGINNED:
            result = self.game.board.check_result()
            if result == MATE:
                if self.game.board.is_check(WHITE):
                    self.state = Game.STATE_BLACK_WINS
                else:
                    self.state = Game.STATE_WHITE_WINS
                self.save()                                
                ongoing = False
            elif result == STALEMATE:
                self.state = Game.STATE_DRAW
                self.save()                
                ongoing = False
        else:
            ongoing = False
        return ongoing
    
    def try_start_game(self):
        '''
        If game is not started, and all players sits, make game started
        '''
        if self.state == self.STATE_CREATED and self.p1_state != Game.PLAYER_UNKNOWN_SIDE and self.p2_state != Game.PLAYER_UNKNOWN_SIDE:
            rest = get_rest_client()
            try:
                rest.dashboard.clearNews(uid=self.p2.id, news_ids=[self.game_invitation])
            except FacebookError:
                try:
                    rest.dashboard.clearNews(uid=self.p1.id, news_ids=[self.game_invitation])
                except FacebookError:
                    pass
                pass
            rest.dashboard.incrementCount(self.white_player.id)
            self.game_invitation = None
            self.state = Game.STATE_BEGINNED
            self.current_move = self.white_player            
            return True
        else:
            return False
        
    
    def publish_game_is_win(self):
        rest = get_rest_client()
        for player in (self.p1, self.p2):
            if (player == self.black_player and self.STATE_BLACK_WINS) or (player==self.white_player and self.STATE_WHITE_WINS):
                rest.stream.publish('Wins a chess match with %s' % self.opponent(player).name, 
                                        action_links=[ {'text': 'View the board', 'href': '%smatch/%d/' % (MY_FB_APP_URL, self.id)},],
                        uid=player.id)
            else:
                rest.stream.publish('Lost a chess match with %s' % self.opponent(player).name, 
                                        action_links=[ {'text': 'View the board', 'href': '%smatch/%d/' % (MY_FB_APP_URL, self.id)},],
                        uid=player.id)
                
    
    def publish_game_is_draw(self):
        rest = get_rest_client()
        
    def publish_game_started(self):
        facebook = get_rest_client()
        for player in (self.p1, self.p2):
            opponent = self.opponent(player)
            facebook.stream.publish('Started a chess game with %s ' % (opponent.name), 
                                    action_links=[ {'text': 'View the match', 'href': '%smatch/%d/' % (MY_FB_APP_URL, self.id)},],
                                    uid=player.id
                                )